#include "vxWorks.h"
#include "motTsecEnd.h"


#define miim_end 200
#define miim_read 100

#ifndef CFG_TBIPA_VALUE
    #define CFG_TBIPA_VALUE	0x1f
#endif
#define MIIMCFG_INIT_VALUE	0x00000003
#define MIIMCFG_RESET		0x80000000

#define MIIMIND_BUSY            0x00000001
#define MIIMIND_NOTVALID        0x00000004

#define MIIM_CONTROL            0x00
#define MIIM_CONTROL_RESET	0x00009140
#define MIIM_CONTROL_INIT       0x00001140
#define MIIM_CONTROL_RESTART    0x00001340
#define MIIM_ANEN               0x00001000

#define MIIM_CR                 0x00
#define MIIM_CR_RST		0x00008000
#define MIIM_CR_INIT	        0x00001000

#define MIIM_STATUS		0x1
#define MIIM_STATUS_AN_DONE 	0x00000020
#define MIIM_STATUS_LINK	0x0004
#define PHY_BMSR_AUTN_ABLE	0x0008
#define PHY_BMSR_AUTN_COMP	0x0020

#define MIIM_PHYIR1		0x2
#define MIIM_PHYIR2		0x3

#define MIIM_ANAR		0x4
#define MIIM_ANAR_INIT		0x1e1

#define MIIM_TBI_ANLPBPA	0x5
#define MIIM_TBI_ANLPBPA_HALF	0x00000040
#define MIIM_TBI_ANLPBPA_FULL	0x00000020

#define MIIM_TBI_ANEX		0x6
#define MIIM_TBI_ANEX_NP	0x00000004
#define MIIM_TBI_ANEX_PRX	0x00000002

#define MIIM_GBIT_CONTROL	0x9
#define MIIM_GBIT_CONTROL_INIT	0xe00


#define MIIM_GBIT_CON		0x09
#define MIIM_GBIT_CON_ADVERT	0x0e00


/* 88E1011 PHY Status Register */
#define MIIM_88E1011_PHY_STATUS         0x11
#define MIIM_88E1011_PHYSTAT_SPEED      0xc000
#define MIIM_88E1011_PHYSTAT_GBIT       0x8000
#define MIIM_88E1011_PHYSTAT_100        0x4000
#define MIIM_88E1011_PHYSTAT_DUPLEX     0x2000
#define MIIM_88E1011_PHYSTAT_SPDDONE	0x0800
#define MIIM_88E1011_PHYSTAT_LINK	0x0400

#define MIIM_88E1011_PHY_SCR		0x10
#define MIIM_88E1011_PHY_MDI_X_AUTO	0x0060

/* 88E1111 PHY LED Control Register */
#define MIIM_88E1111_PHY_LED_CONTROL   24
#define MIIM_88E1111_PHY_LED_DIRECT    0x4100
#define MIIM_88E1111_PHY_LED_COMBINE   0x411C


#define MIIM_READ_COMMAND       0x00000001

#define MRBLR_INIT_SETTINGS	PKTSIZE_ALIGN

#define MINFLR_INIT_SETTINGS	0x00000040

#define DMACTRL_INIT_SETTINGS   0x000000c3
#define DMACTRL_GRS             0x00000010
#define DMACTRL_GTS             0x00000008

#define TSTAT_CLEAR_THALT       0x80000000
#define RSTAT_CLEAR_RHALT       0x00800000

#define MACCFG2_INIT_SETTINGS	0x00007205
#define MACCFG2_FULL_DUPLEX	0x00000001
#define MACCFG2_IF              0x00000300
#define MACCFG2_GMII		0x00000200
#define MACCFG2_MII             0x00000100

#define ECNTRL_INIT_SETTINGS	0x00001000
#define ECNTRL_TBI_MODE         0x00000020
#define ECNTRL_R100		0x00000008
#define ECNTRL_SGMII_MODE	0x00000002




uint mii_parse_88E1011_psr(uint mii_reg, TSEC_DRV_CTRL *pDrvCtrl)
{
	uint speed;

	mii_reg = read_phy_reg(pDrvCtrl, MIIM_88E1011_PHY_STATUS);

	if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) &&
		!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {
		int i = 0;

		printf("Waiting for PHY realtime link");
		while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) {
			/* Timeout reached ? */
			if (i > 5000) {
				printf(" TIMEOUT !\n");
				pDrvCtrl->link = 0;
				break;
			}

			if ((i++ % 1000) == 0) {
				printf('.');
			}
			udelay(1000);	/* 1 ms */
			mii_reg = read_phy_reg(pDrvCtrl, MIIM_88E1011_PHY_STATUS);
		}
		printf(" done\n");
		udelay(500000);	/* another 500 ms (results in faster booting) */
	} else {
		if (mii_reg & MIIM_88E1011_PHYSTAT_LINK)
			pDrvCtrl->link = 0x01;
		else
			pDrvCtrl->link = 0;
	}

	if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
		pDrvCtrl->duplexity = 0x01;
	else
		pDrvCtrl->duplexity = 0;

	speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);

	switch (speed) {
	case MIIM_88E1011_PHYSTAT_GBIT:
		pDrvCtrl->speed = 1000;
		break;
	case MIIM_88E1011_PHYSTAT_100:
		pDrvCtrl->speed = 100;
		break;
	default:
		pDrvCtrl->speed = 10;
	}

	return 0;
}


static void adjust_link(TSEC_DRV_CTRL *pDrvCtrl )
{
	
	volatile  TSEC_REG_T *phyregs = pDrvCtrl->tsecRegsPtr;


	if (pDrvCtrl->link) {
		if (pDrvCtrl->duplexity != 0)
			phyregs->maccfg2 |= MACCFG2_FULL_DUPLEX;
		else
			phyregs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX);

		switch (pDrvCtrl->speed) {
		case 1000:
			phyregs->maccfg2 = ((phyregs->maccfg2 & ~(MACCFG2_IF))
					 | MACCFG2_GMII);
			break;
		case 100:
		case 10:
			phyregs->maccfg2 = ((phyregs->maccfg2 & ~(MACCFG2_IF))
					 | MACCFG2_MII);

			/* Set R100 bit in all modes although
			 * it is only used in RGMII mode
			 */
			if (pDrvCtrl->speed == 100)
				phyregs->ecntrl |= ECNTRL_R100;
			else
				phyregs->ecntrl &= ~(ECNTRL_R100);
			break;
		default:
			printf(" Speed was bad\n");
			break;
		}

		printf("Speed: %d, %s duplex\n", pDrvCtrl->speed,
		       (pDrvCtrl->duplexity) ? "full" : "half");

	} else {
		printf("No link.\n");
	}
}

void write_phy_reg(TSEC_DRV_CTRL *pDrvCtrl, uint regnum, uint value)
{
	volatile  TSEC_REG_T *phyregs = pDrvCtrl->tsecMiiPtr;
	uint phyid = pDrvCtrl->phyAddr;
	int timeout = 1000000;

	phyregs->miimadd = (phyid << 8) | regnum;
	phyregs->miimcon = value;
	/*asm("sync");*/

	timeout = 1000000;
	while ((phyregs->miimind & MIIMIND_BUSY) && timeout--) ;
}

/* Reads register regnum on the device's PHY through the
 * registers specified in priv.	 It lowers and raises the read
 * command, and waits for the data to become valid (miimind
 * notvalid bit cleared), and the bus to cease activity (miimind
 * busy bit cleared), and then returns the value
 */
uint read_phy_reg( TSEC_DRV_CTRL *pDrvCtrl, uint regnum)
{
	uint value;
	volatile  TSEC_REG_T *phyregs = pDrvCtrl->tsecMiiPtr;
	uint phyid = pDrvCtrl->phyAddr;


	/* Put the address of the phy, and the register
	 * number into MIIMADD */
	phyregs->miimadd = (phyid << 8) | regnum;

	/* Clear the command register, and wait */
	phyregs->miicom = 0;
	/*asm("sync");*/

	/* Initiate a read command, and wait */
	phyregs->miicom = MIIM_READ_COMMAND;
/*	asm("sync");*/

	/* Wait for the the indication that the read is done */
	while ((phyregs->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ;

	/* Grab the value read from the PHY */
	value = phyregs->miimstat;

	return value;
}

unsigned int mii_cr_init(uint mii_reg, TSEC_DRV_CTRL *pDrvCtrl)
{
	if (pDrvCtrl->phyFlags & 0x01)
		return MIIM_CONTROL_INIT;
	else
		return MIIM_CR_INIT;
}

unsigned int mii_parse_sr(uint mii_reg, TSEC_DRV_CTRL *pDrvCtrl)
{
	/*
	 * Wait if the link is up, and autonegotiation is in progress
	 * (ie - we're capable and it's not done)
	 */
	mii_reg = read_phy_reg(pDrvCtrl, MIIM_STATUS);
	
	if ((mii_reg & MIIM_STATUS_LINK) && (mii_reg & PHY_BMSR_AUTN_ABLE)
	    && !(mii_reg & PHY_BMSR_AUTN_COMP)) {
		int i = 0;

		printf("\n\r Waiting for PHY auto negotiation to complete");
		while (!(mii_reg & PHY_BMSR_AUTN_COMP)) {
			/*
			 * Timeout reached ?
			 */
			if (i > 5000) {
				printf(" TIMEOUT !\n");
				pDrvCtrl->link = 0;
				return 0;
			}

			if ((i++ % 1000) == 0) {
				printf('.');
			}
			udelay(1000);	/* 1 ms */
			mii_reg = read_phy_reg(pDrvCtrl, MIIM_STATUS);
		}
		printf(" done\n");
		pDrvCtrl->link = 1;
		udelay(500000);	/* another 500 ms (results in faster booting) */
	} else {
		if (mii_reg & MIIM_STATUS_LINK)
			pDrvCtrl->link = 1;
		else
			pDrvCtrl->link = 0;
	}

	return 0;
}


void phy_run_commands
	(
		TSEC_DRV_CTRL *pDrvCtrl, 
		PHY_CMD *cmd
	)
{
	int i;
	uint result;
	volatile  TSEC_REG_T *phyregs = pDrvCtrl->tsecMiiPtr;

	phyregs->miicfg = MIIMCFG_RESET;

	phyregs->miicfg = MIIMCFG_INIT_VALUE;

	while (phyregs->miimind & MIIMIND_BUSY) ;

	for (i = 0; cmd->mii_reg != miim_end; i++) {
		if (cmd->mii_data == miim_read) {
			result = read_phy_reg(pDrvCtrl, cmd->mii_reg);

			if (cmd->funct != NULL)
				(*(cmd->funct)) (result, pDrvCtrl);

		} else {
			if (cmd->funct != NULL)
				result = (*(cmd->funct)) (cmd->mii_reg, pDrvCtrl);
			else
				result = cmd->mii_data;

			write_phy_reg(pDrvCtrl, cmd->mii_reg, result);

		}
		cmd++;
	}
	return;
}

static int init_phy( TSEC_DRV_CTRL *pDrvCtrl)
{
	TSEC_REG_T * tsecMiiReg = pDrvCtrl->tsecMiiPtr;
	
	/* Assign a Physical address to the TBI */
	tsecMiiReg->tbipa = CFG_TBIPA_VALUE;


	/* Reset MII (due to new addresses) */
	tsecMiiReg->miicfg = MIIMCFG_RESET;
	
	tsecMiiReg->miicfg = MIIMCFG_INIT_VALUE;
	
	while (tsecMiiReg->miimind & MIIMIND_BUSY) ;

	/* Get the cmd structure corresponding to the attached
	 * PHY */
	
	
	pDrvCtrl->phyInfo = get_phy_info();
	

	phy_run_commands(pDrvCtrl, pDrvCtrl->phyInfo->config);
	

	return 1;
}

PHY_INFO1 phy_info_M88E1111S = {
	0x01410cc,
	"Marvell 88E1111S",
	4,
	(PHY_CMD[]){	/* config */
			   /* Reset and configure the PHY */
			   {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
			   {0x14, 0x0cd0, NULL}, /* Delay RGMII RX */
			   {0x18, 0x4109, NULL}, /* Set LED_LNK and LED_TX */
			   {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL},
			   {MIIM_ANAR, MIIM_ANAR_INIT, NULL},
			   {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},
			   {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init},
			   {miim_end,}
			   },
	(PHY_CMD[]){	/* startup */
			   /* Status is read once to clear old link state */
			   {MIIM_STATUS, miim_read, NULL},
			   /* Auto-negotiate */
			   {MIIM_STATUS, miim_read, &mii_parse_sr},
			   /* Read the status */
			   {MIIM_88E1011_PHY_STATUS, miim_read,
			    &mii_parse_88E1011_psr},
			   {miim_end,}
			   },
	(PHY_CMD[]){	/* shutdown */
			   {miim_end,}
			   },
};
PHY_INFO1 *get_phy_info(void)
{		
	return &phy_info_M88E1111S;
}

